package com.kexio.auth.interceptor;

import java.util.concurrent.TimeUnit;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import cn.dev33.satoken.stp.StpUtil;

/**
 * Kexio安全增强拦截器
 * 
 * 设计理念：增量优化，不影响Sa-Token原生功能
 * 
 * 功能范围：
 * 1. 敏感路径访问审计日志
 * 2. 高风险接口频率限制
 * 3. IP访问控制和用户行为分析
 * 4. 安全事件监控和告警
 * 
 * 工作原理：
 * - 在Sa-Token原生拦截器执行完毕后运行（order=200）
 * - 仅在用户已通过Sa-Token认证后添加增强安全检查
 * - 不干扰Sa-Token的任何原生功能（@SaIgnore、@SaCheckLogin等）
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Component
public class KexioSecurityEnhanceInterceptor implements HandlerInterceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(KexioSecurityEnhanceInterceptor.class);
    
    // Redis模板，用于频率限制检查
    @Autowired(required = false)
    private RedisTemplate<String, Object> redisTemplate;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        String requestPath = request.getRequestURI();
        String clientIP = getClientIP(request);
        String userAgent = request.getHeader("User-Agent");
        String method = request.getMethod();
        
        try {
            // 🎯 重要：仅在用户已登录时进行增强安全检查
            // Sa-Token已经处理了所有的认证逻辑，我们只做增强
            if (StpUtil.isLogin()) {
                Object loginId = StpUtil.getLoginId();
                
                // 1️⃣ 敏感路径访问审计日志
                if (isSensitivePath(requestPath)) {
                    logger.info("🔐 敏感路径访问: method={}, path={}, userId={}, ip={}, userAgent={}", 
                               method, requestPath, loginId, clientIP, 
                               userAgent != null ? userAgent.substring(0, Math.min(50, userAgent.length())) : "null");
                }
                
                // 2️⃣ 高风险接口频率限制
                if (isHighRiskPath(requestPath)) {
                    checkRateLimit(loginId.toString(), clientIP);
                }
                
                // 3️⃣ 可扩展：其他安全增强功能
                // - IP白名单检查
                // - 用户行为异常检测
                // - 设备指纹验证
                // - 地理位置异常检测
                performAdditionalSecurityChecks(loginId.toString(), clientIP, userAgent, requestPath);
            }
            
            return true; // 继续处理请求
            
        } catch (Exception e) {
            // 安全增强检查失败时的处理
            logger.warn("🚨 Kexio安全增强检查失败: method={}, path={}, ip={}, error={}", 
                       method, requestPath, clientIP, e.getMessage());
            
            // 根据错误类型决定是否阻止请求
            if (e instanceof SecurityException) {
                // 安全异常，阻止请求
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                response.getWriter().write("{\"code\":403,\"message\":\"安全检查失败: " + e.getMessage() + "\"}");
                return false;
            } else {
                // 其他异常，记录日志但不阻止请求（确保业务可用性）
                return true;
            }
        }
    }
    
    /**
     * 获取客户端真实IP地址
     */
    private String getClientIP(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        
        // 处理多IP情况，取第一个
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        
        return ip;
    }
    
    /**
     * 判断是否为敏感路径（需要记录详细日志）
     */
    private boolean isSensitivePath(String path) {
        return path.contains("/admin/") ||           // 管理员操作
               path.contains("/cache/monitor/") ||   // 缓存监控
               path.contains("/system/") ||          // 系统管理
               path.contains("/user/delete") ||      // 用户删除
               path.contains("/permission/") ||      // 权限管理
               path.contains("/role/") ||            // 角色管理
               path.contains("/config/");            // 配置管理
    }
    
    /**
     * 判断是否为高风险路径（需要频率限制）
     */
    private boolean isHighRiskPath(String path) {
        return path.contains("/delete") ||           // 删除操作
               path.contains("/batch") ||            // 批量操作
               path.contains("/export") ||           // 数据导出
               path.contains("/import") ||           // 数据导入
               path.contains("/admin/") ||           // 管理员操作
               path.contains("/cache/") ||           // 缓存操作
               path.endsWith("/create-test-data");   // 测试数据创建
    }
    
    /**
     * 检查访问频率限制
     */
    private void checkRateLimit(String userId, String clientIP) {
        if (redisTemplate == null) {
            return; // Redis不可用时跳过频率检查
        }
        
        try {
            String rateLimitKey = "kexio:security:rate_limit:user:" + userId + ":ip:" + clientIP;
            
            // 使用Redis incr实现简单的滑动窗口频率限制
            Long currentCount = redisTemplate.opsForValue().increment(rateLimitKey);
            
            if (currentCount == null) {
                return;
            }
            
            if (currentCount == 1) {
                // 首次访问，设置1分钟过期
                redisTemplate.expire(rateLimitKey, 1, TimeUnit.MINUTES);
            }
            
            // 1分钟内超过30次请求则限制
            if (currentCount > 30) {
                logger.warn("🚨 Kexio频率限制触发: userId={}, ip={}, count={}", userId, clientIP, currentCount);
                throw new SecurityException("请求过于频繁，请稍后再试");
            }
            
        } catch (SecurityException e) {
            throw e; // 重新抛出安全异常
        } catch (Exception e) {
            // Redis异常不应阻断正常业务，仅记录日志
            logger.warn("频率限制检查失败: userId={}, ip={}, error={}", userId, clientIP, e.getMessage());
        }
    }
    
    /**
     * 执行其他安全增强检查
     * 
     * 这里可以添加更多的安全检查逻辑：
     * - IP白名单/黑名单检查
     * - 用户行为异常检测
     * - 设备指纹验证
     * - 地理位置异常检测
     * - 登录时间窗口限制
     */
    private void performAdditionalSecurityChecks(String userId, String clientIP, String userAgent, String requestPath) {
        // 预留扩展点
        // 可以根据业务需求添加更多安全检查
        
        // 示例：检查是否在工作时间访问（可配置）
        // checkWorkingHoursAccess(userId, requestPath);
        
        // 示例：检查异常登录地点
        // checkAbnormalLocation(userId, clientIP);
        
        // 示例：检查设备指纹
        // checkDeviceFingerprint(userId, userAgent);
    }
}
